package com.ants.boot.core.security;

import com.ants.boot.core.security.handle.LoginFailureHandler;
import com.ants.boot.core.security.jwt.JwtTokenProvider;
import com.ants.boot.core.security.service.CustomUserDetailsServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;


/**
 * @author 蚂蚁会花呗
 * 请求过滤器
 * @date 2022/2/14 17:54
 * 统一请求过滤器，除白名单外，（白名单在WebSecurityConfig类中配置）任何请求都要经过该过滤器
 */
public class RequestJwtAuthenticationFilter extends OncePerRequestFilter {

    Log logger = LogFactory.getLog(RequestJwtAuthenticationFilter.class);

    private static final PathMatcher pathmatcher = new AntPathMatcher();

    /**
     * 过滤器放行的url列表
     */
    private List<String> permissiveUrlList;


    /**
     * jwtTokenProvider 方法提供类
     */
    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    /**
     * spring security 登录实现
     */
    @Autowired
    private CustomUserDetailsServiceImpl sysUserDetailsService;

    @Autowired
    private LoginFailureHandler loginFailureHandler;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (isPermissiveUrl(request)){
            filterChain.doFilter(request, response);
            return;
        }
        String token = getJwtToken(request);
        AuthenticationException failed = null;
        if (token != null && jwtTokenProvider.validateToken(token)){
            //判断是否登录超时
            Map<String,Object> map = jwtTokenProvider.isTokenExpiredAndRefreshToken(token);
            if (!map.isEmpty()){
                if (Boolean.parseBoolean(map.get("status").toString())){
                    if (map.containsKey("token")){
                        token = map.get("token").toString();
                        response.setHeader("Authorization", token);
                    }
                    String username = jwtTokenProvider.getUserNameFromToken(token);
                    UserDetails userDetails = sysUserDetailsService.loadUserByUsername(username);
                    Authentication authentication = new UsernamePasswordAuthenticationToken(
                            userDetails,null,userDetails.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }else{
                    failed = new InsufficientAuthenticationException("登录超时，请重新登录！");
                    unsuccessfulAuthentication(request, response, failed);
                    return;
                }
            }else{
                failed = new InsufficientAuthenticationException("登录超时，请重新登录！");
                unsuccessfulAuthentication(request, response, failed);
                return;
            }
        }else{
            failed = new InsufficientAuthenticationException("Authorization不能为空，或格式不正确！");
            unsuccessfulAuthentication(request, response, failed);
            return;
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 登录失败情况
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response, AuthenticationException failed)
            throws IOException, ServletException {
        SecurityContextHolder.clearContext();
        loginFailureHandler.onAuthenticationFailure(request, response, failed);
    }

    /**
     * 获取header 中的  Authorization
     * @param request
     * @return
     */
    protected String getJwtToken(HttpServletRequest request) {
        String authInfo = request.getHeader("Authorization");
        return StringUtils.removeStart(authInfo, "Bearer ");
    }


    /**
     * 设置放行的url
     * @param urls
     */
    public void setPermissiveUrl(String... urls) {
        if(permissiveUrlList == null){
            permissiveUrlList = new ArrayList<>();
        }
        permissiveUrlList.addAll(Arrays.asList(urls));
    }


    /**
     * 判断是否放行
     * @param request
     * @return
     */
    private boolean isPermissiveUrl(HttpServletRequest request) {
        boolean flag = false;
        for (String url : permissiveUrlList){
            if (pathmatcher.match(url, request.getServletPath())) {
                return true;
            }
        }
        return false;
    }

}
